#include <task.h>
#include <desc.h>
#include <share.h>
#include <8259A.h>
#include <app.h>


volatile TASK*          current_task = NULL;            // 当前任务指针，永远指向当前任务
volatile U32            current_reg;                    // 当前任务的上下文起始位置
QUEUE                   TASK_READY_QUEUE;               // 就绪任务队列
QUEUE                   TASK_WAIT_QUEUE;                // 等待任务队列
static TASK_OOP         taskOop[MAX_TASK_NUM] = {0};    // 普通任务对象数组 
static TASK_OOP         taskIdle = {0};                 // 空闲任务对象               


/******************************************************************************
* 函数名称: SwitchTo(TASK* task)
* 功能说明: 切换到任务 task 执行
* 输入参数: TASK*	task	--任务指针
* 输出参数: 无
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET SwitchTo(TASK* task)
{
    // 取任务上下文(寄存器)基址
    U32* reg_base = (U32*)(&task->reg);

    // 检查参数合法性
    if(NULL == task || NULL == reg_base)
        return E_ERR;
    
    // 恢复上下文
    asm volatile(
                "movl %0, %%esp\n"      // 先将栈顶指针 esp 指向任务上下文 reg 的起始位置
                "popl %%gs\n"
                "popl %%fs\n"
                "popl %%es\n"
                "popl %%ds\n"
                
                "popal\n"               // popal(gcc) = popad(nasm)
                "addl $4, %%esp\n"      // 跳过 err_code
                "iret\n"
                :
                : "r"(reg_base)         // %0 替换成 reg_base
                :
                );
}

/******************************************************************************
* 函数名称: static void TaskEntry(void)
* 功能说明: 任务入口
* 输入参数: 无
* 输出参数: 无
* 函数返回: 无
* 其它说明: 无
******************************************************************************/
static void TaskEntry(void)
{
    if(current_task)
    {
        current_task->task_entry();
    }
    // 任务销毁工作
    asm volatile(
            "movl $0, %%eax\n"
            "int $0x80\n"
            : : : "eax" // 告诉 gcc 编译器，eax 寄存器被内嵌汇编使用，需要 gcc 自动添加保护和恢复操作（入栈和出栈）
        );

    while(1);
}

/******************************************************************************
* 函数名称: E_RET TaskCreat(TASK* task, TASK_FUNC pFunc, U08* stackAddr, U16 stackSize, U08* name, E_TASK_PRI priority)
* 功能说明: 创建任务
* 输入参数: TASK_FUNC pFunc     --任务函数
    　　　　U08* stackAddr      --任务栈基址
    　　　　U16 stackSize       --任务栈大小
    　　　　U08* name           --任务名称
    　　　　E_TASK_PRI priority --任务优先级   
* 输出参数: TASK*	task        --任务指针
* 函数返回: E_OK:成功; E_ERR:失败
* 其它说明: 无
******************************************************************************/
E_RET TaskCreat(TASK* task, TASK_FUNC pFunc, U08* stackAddr, U16 stackSize, U08* name, E_TASK_PRI priority)
{
    // 检查参数合法性
    if(NULL == task || NULL == pFunc || NULL == stackAddr || 0 == stackSize)
        return E_ERR;

    task->name = name;
    task->stack_addr = stackAddr;
    task->stack_size = stackSize;

    task->reg.cs = LDT_CODE32_SEL;
    task->reg.gs = LDT_VIDEO_SEL;
    task->reg.ds = LDT_DATA32_SEL;
    task->reg.es = LDT_DATA32_SEL;
    task->reg.fs = LDT_DATA32_SEL;
    task->reg.ss = LDT_DATA32_SEL;
    task->reg.esp = (U32)task->stack_addr + task->stack_size; // 栈顶
    task->reg.eip = (U32)TaskEntry;
    task->reg.eflags = 0x3202;  // IOPL=3: 允许任务（特权级 3）进行 /O 操作; IF=1: 允许外部中断
    task->task_entry = pFunc;
    task->priority = priority;
    task->total = 256 - priority;
    current_task->current = 0;
    
    return E_OK;
}

/******************************************************************************
* 函数名称: void TaskStart(void)
* 功能说明: 启动任务
* 输入参数: 无
* 输出参数: 无
* 函数返回: 无
* 其它说明: 想要启动所有任务，只要启动第一个任务就可以了，其它任务将由任务调度启动
******************************************************************************/
void TaskStart(void)
{
    current_task = &taskIdle.task;                                               // 当前任务指针指向空闲任务
    TSS* tss = (TSS*)(*(U32*)TSS_ENTRY_ADDR);                               // 找到 TSS
    tss->esp0 = (U32)&current_task->reg + sizeof(current_task->reg);        // TSS.esp0 指向空闲任务 taskIdle 的上下文数据结构 reg 的末尾   
    current_reg = (U32)&current_task->reg;                                  // current_reg 指向空闲任务 taskIdle 上下文数据结构 reg 的起始位置
    
    enable_int0x20();                                                       // 取消屏蔽 0x20 号时钟中断
    
    // 寄存器 cr0 的 PG 位置 1，启动内存分页机制
    asm volatile(
                "movl %%cr0, %%eax\n"         
                "or $0x80000000, %%eax\n"   
                "movl %%eax, %%cr0\n"          
                :
                :
                : "eax"
                );
    
    asm volatile("sti");                                                    // 执行任务前必须开中断
    SWITCH_TO(current_task);                                                // 切换到空闲任务 taskIdle 执行
}

/******************************************************************************
* 函数名称: void TaskInit(void)
* 功能说明: 任务初始化
* 输入参数: 无
* 输出参数: 无
* 函数返回: 无
* 其它说明: 无
******************************************************************************/
void TaskInit(void)
{
    U16 index = 0;
    U16 taskNum = 0;
    APP_INFO* pAppInfo = (APP_INFO *)(*(U32 *)APP_INFO_ADDR);
    U32 appNum = *((U32*)(APP_NUM_ADDR));
    U08* IdleTaskStackAddr = (U08 *)(*(U32 *)IDLE_TASK_STACK_ADDR);
    U16 IdleTaskStackSize = (U16)(*((U32*)(IDLE_TASK_STACK_SIZE)));
    TASK_FUNC pIdleTaskFunc = (TASK_FUNC)(*(U32 *)IDLE_TASK_FUNC_ADDR);

    QueueInit(&TASK_READY_QUEUE);                  // 就绪任务队列初始化
    QueueInit(&TASK_WAIT_QUEUE);                   // 等待任务队列初始化
    // 创建第一个任务（空闲任务）
    // TaskCreat(&taskIdle.task, TaskIdleFunc, taskIdle_stack, IDLE_STACK_SIZE, "Idle", E_TASK_PRI15);
    TaskCreat(&taskIdle.task, pIdleTaskFunc, IdleTaskStackAddr, IdleTaskStackSize, "Idle", E_TASK_PRI15);
    // 将空闲任务节点添加到就绪任务队列中
    QueueAdd(&TASK_READY_QUEUE, (QUEUE_NODE *)&taskIdle);

    for(index = 0; index < MAX_TASK_NUM && taskNum < appNum; index++)
    {
        if(0 == taskOop[index].active)
        {
            taskOop[index].active = 1;
            taskOop[index].task.name = pAppInfo[taskNum].name;
            taskOop[index].task.stack_addr = pAppInfo[taskNum].stackAddr;
            taskOop[index].task.stack_size = pAppInfo[taskNum].stackSize;
            taskOop[index].task.task_entry = pAppInfo[taskNum].pfunc;
            taskOop[index].task.priority = pAppInfo[taskNum].priority;
            
            TaskCreat(&taskOop[index].task, taskOop[index].task.task_entry, taskOop[index].task.stack_addr, taskOop[index].task.stack_size, taskOop[index].task.name, taskOop[index].task.priority);
            QueueAdd(&TASK_READY_QUEUE, (QUEUE_NODE *)&taskOop[index]);
            taskNum++; 
        }
    }
}

/******************************************************************************
* 函数名称: void TaskDestory(void)
* 功能说明: 销毁当前任务
* 输入参数: 无
* 输出参数: 无
* 函数返回: 无
* 其它说明: 无
******************************************************************************/
E_RET TaskDestory(void)
{ 
    QUEUE_NODE* node = NULL;

    // 把当前任务节点从就绪任务队列中取出，当前任务节点在队列尾
    node = QueueTailRemove(&TASK_READY_QUEUE);
    if(NULL == node)
        return E_ERR;

    ((TASK_OOP *)QUEUE_NODE(node, TASK_OOP, QueueNode))->active = 0;

    // 从就绪任务队列中取出一个任务节点并执行该任务，再将该任务节点重新添加到就绪任务队列中
    node = QueueRemove(&TASK_READY_QUEUE);
    current_task = (volatile TASK *)&(((TASK_OOP *)QUEUE_NODE(node, TASK_OOP, QueueNode))->task);
    QueueAdd(&TASK_READY_QUEUE, node);

    TSS* tss = (TSS*)(*(U32*)TSS_ENTRY_ADDR);                             // 找到 TSS
    tss->esp0 = (U32)(&current_task->reg) + sizeof(current_task->reg);    // TSS.esp0 指向任务上下文数据结构 reg 的末尾
    current_reg = (U32)(&current_task->reg);                              // current_reg 指向任务上下文数据结构 reg 的起始位置
    SWITCH_TO(current_task);
    
    return E_OK;
}